package com.hys.app.converter.erp;

import com.hys.app.framework.database.WebPage;
import com.hys.app.model.erp.dos.*;
import com.hys.app.model.erp.dto.StockUpdateDTO;
import com.hys.app.model.erp.dto.WarehouseOutDTO;
import com.hys.app.model.erp.enums.StockOperateEnum;
import com.hys.app.model.erp.vo.WarehouseOutAllowable;
import com.hys.app.model.erp.vo.WarehouseOutVO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingConstants;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.hys.app.framework.util.CollectionUtils.convertList;
import static com.hys.app.framework.util.CollectionUtils.convertMap;

/**
 * 出库单 Convert
 *
 * @author 张崧
 * @since 2023-12-07 16:50:20
 */
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
public interface WarehouseOutConverter {

    WarehouseOutDO convert(WarehouseOutDTO warehouseOutDTO);

    @Mapping(target = "allowable", expression = "java(this.getAllowable(warehouseOutDO))")
    WarehouseOutVO convert(WarehouseOutDO warehouseOutDO);

    WebPage<WarehouseOutVO> convert(WebPage<WarehouseOutDO> webPage);

    /**
     * 组装出库单
     *
     * @param warehouseOutDTO 出库单DTO
     * @return 出库单
     */
    default WarehouseOutDO combination(WarehouseOutDTO warehouseOutDTO) {
        WarehouseOutDO warehouseOutDO = new WarehouseOutDO();

        Collection<OrderDO> orderList = warehouseOutDTO.getOrderMap().values();
        OrderDO orderDO = orderList.iterator().next();

        warehouseOutDO.setId(warehouseOutDTO.getId());
        warehouseOutDO.setOutTime(warehouseOutDTO.getOutTime());
        warehouseOutDO.setWarehouseConsigneeId(warehouseOutDTO.getWarehouseConsigneeId());
        warehouseOutDO.setWarehouseConsigneeName(warehouseOutDTO.getWarehouseConsignee().getRealName());
        warehouseOutDO.setDeptId(orderDO.getDeptId());
        warehouseOutDO.setDeptName(orderDO.getDeptName());
        warehouseOutDO.setWarehouseId(orderDO.getWarehouseId());
        warehouseOutDO.setWarehouseName(orderDO.getWarehouseName());
        warehouseOutDO.setMemberRealName(orderDO.getMemberName());
        warehouseOutDO.setMemberMobile(orderDO.getMemberMobile());
        warehouseOutDO.setDeliveryType(orderDO.getDeliveryType());
        warehouseOutDO.setOrderIdList(convertList(orderList, OrderDO::getId));
        warehouseOutDO.setOrderSnList(convertList(orderList, OrderDO::getSn));
        warehouseOutDO.setStoreId(orderDO.getStoreId());
        warehouseOutDO.setStoreName(orderDO.getStoreName());

        return warehouseOutDO;
    }

    default WebPage<WarehouseOutVO> combination(WebPage<WarehouseOutDO> webPage, Map<Long, String> deptNameMap, Map<Long, String> warehouseNameMap) {
        WebPage<WarehouseOutVO> page = convert(webPage);

        for (WarehouseOutVO warehouseOutVO : page.getData()) {
            warehouseOutVO.setDeptName(deptNameMap.get(warehouseOutVO.getDeptId()));
            warehouseOutVO.setWarehouseName(warehouseNameMap.get(warehouseOutVO.getWarehouseId()));
        }

        return page;
    }

    default WarehouseOutAllowable getAllowable(WarehouseOutDO warehouseOutDO){
        return new WarehouseOutAllowable(warehouseOutDO);
    }

    default List<StockUpdateDTO> convertStockUpdateList(List<WarehouseOutItemDO> itemList){
        return itemList.stream().map(warehouseOutItemDO -> {
            StockUpdateDTO stockUpdateDTO = new StockUpdateDTO();

            stockUpdateDTO.setBatchId(warehouseOutItemDO.getWarehouseEntryBatchId());
            stockUpdateDTO.setTip(warehouseOutItemDO.getWarehouseEntryBatchSn());
            stockUpdateDTO.setOperate(StockOperateEnum.Reduce);
            stockUpdateDTO.setChangeNum(warehouseOutItemDO.getOutNum());

            return stockUpdateDTO;
        }).collect(Collectors.toList());
    }

    default WarehouseOutVO convert(WarehouseOutDO warehouseOutDO, List<WarehouseOutItemDO> allItemList, List<WarehouseEntryBatchDO> batchList){
        WarehouseOutVO warehouseOutVO = convert(warehouseOutDO);

        // 批次
        Map<Long, WarehouseEntryBatchDO> batchMap = convertMap(batchList, WarehouseEntryBatchDO::getId, Function.identity());
        // 出库明细
        warehouseOutVO.setItemList(allItemList);
        // 出库明细按照订单分组
        Map<Long, List<WarehouseOutItemDO>> itemListMap = allItemList.stream().collect(Collectors.groupingBy(WarehouseOutItemDO::getOrderId));
        warehouseOutVO.setOrderList(convertList(itemListMap.values(), itemList -> convertWarehouseOutOrder(itemList, batchMap)));

        return warehouseOutVO;
    }

    default WarehouseOutVO.Order convertWarehouseOutOrder(List<WarehouseOutItemDO> itemList, Map<Long, WarehouseEntryBatchDO> batchMap){
        WarehouseOutVO.Order warehouseOutOrderVO = new WarehouseOutVO.Order();

        warehouseOutOrderVO.setOrderId(itemList.get(0).getOrderId());
        warehouseOutOrderVO.setOrderSn(itemList.get(0).getOrderSn());

        // 按照订单明细id分组
        Map<Long, List<WarehouseOutItemDO>> itemListMap = itemList.stream().collect(Collectors.groupingBy(WarehouseOutItemDO::getOrderItemId));
        warehouseOutOrderVO.setOrderItemList(convertList(itemListMap.values(), list -> convertWarehouseOutOrderItem(list, batchMap)));

        return warehouseOutOrderVO;
    }

    default WarehouseOutVO convertPreview(List<OrderDO> orderList, List<OrderItemDO> orderItemList, Map<Long, List<WarehouseEntryBatchDO>> batchMap){
        Map<Long, List<OrderItemDO>> orderItemListMap = orderItemList.stream().collect(Collectors.groupingBy(OrderItemDO::getOrderId));

        // 组装数据返回
        WarehouseOutVO warehouseOutVO = new WarehouseOutVO();
        warehouseOutVO.setWarehouseId(orderList.get(0).getWarehouseId());
        warehouseOutVO.setWarehouseName(orderList.get(0).getWarehouseName());
        warehouseOutVO.setOrderList(convertList(orderList, orderDO -> convertWarehouseOutOrder(orderDO, orderItemListMap.get(orderDO.getId()), batchMap)));
        return warehouseOutVO;
    }

    default WarehouseOutVO.Order convertWarehouseOutOrder(OrderDO orderDO, List<OrderItemDO> orderItemList, Map<Long, List<WarehouseEntryBatchDO>> batchMap){
        WarehouseOutVO.Order order = new WarehouseOutVO.Order();

        order.setOrderId(orderDO.getId());
        order.setOrderSn(orderDO.getSn());
        order.setOrderItemList(convertList(orderItemList, orderItemDO -> convertWarehouseOutOrderItem(orderItemDO, batchMap.get(orderItemDO.getProductId()))));

        return order;
    }

    default WarehouseOutVO.OrderItem convertWarehouseOutOrderItem(OrderItemDO orderItemDO, List<WarehouseEntryBatchDO> batchList){
        WarehouseOutVO.OrderItem orderItem = new WarehouseOutVO.OrderItem();

        orderItem.setOrderItemId(orderItemDO.getId());
        orderItem.setProductId(orderItemDO.getProductId());
        orderItem.setProductSn(orderItemDO.getProductSn());
        orderItem.setProductName(orderItemDO.getProductName());
        orderItem.setProductSpecification(orderItemDO.getProductSpecification());
        orderItem.setProductUnit(orderItemDO.getProductUnit());
        orderItem.setNum(orderItemDO.getNum());

        // 该产品需要出库的数量
        int productOutNum = orderItemDO.getNum();
        // 循环批次的剩余库存，直到数量达到出库数量
        List<WarehouseOutVO.StockBatch> usedBatch = new ArrayList<>();
        if (batchList != null) {
            for (WarehouseEntryBatchDO warehouseEntryBatchDO : batchList) {
                // 批次剩余数量如果为0，继续找其他批次
                if(warehouseEntryBatchDO.getRemainNum() == 0){
                    continue;
                }

                // 计算该批次出库的数量
                int batchOutNum = Math.min(productOutNum, warehouseEntryBatchDO.getRemainNum());
//                // 减少该批次剩余数量（这里需要把剩余库存减掉，其他订单如果也存在相同的商品，还会用到该批次）
//                warehouseEntryBatchDO.setRemainNum(warehouseEntryBatchDO.getRemainNum() - batchOutNum);
                // 加入使用的批次列表
                usedBatch.add(convert(warehouseEntryBatchDO, batchOutNum));
                // 减掉商品数量，如果减到0，就跳出循环
                productOutNum = productOutNum - batchOutNum;
                if (productOutNum == 0) {
                    break;
                }
            }
        }

        // 设置该商品出库时使用的批次
        orderItem.setBatchList(usedBatch);

        return orderItem;
    }
    default WarehouseOutVO.OrderItem convertWarehouseOutOrderItem(List<WarehouseOutItemDO> itemList, Map<Long, WarehouseEntryBatchDO> batchMap){
        WarehouseOutVO.OrderItem orderItem = new WarehouseOutVO.OrderItem();

        WarehouseOutItemDO warehouseOutItemDO = itemList.get(0);
        orderItem.setOrderItemId(warehouseOutItemDO.getOrderItemId());
        orderItem.setProductId(warehouseOutItemDO.getProductId());
        orderItem.setProductSn(warehouseOutItemDO.getProductSn());
        orderItem.setProductName(warehouseOutItemDO.getProductName());
        orderItem.setProductSpecification(warehouseOutItemDO.getProductSpecification());
        orderItem.setProductUnit(warehouseOutItemDO.getProductUnit());
        orderItem.setNum(itemList.stream().mapToInt(WarehouseOutItemDO::getOutNum).sum());

        // 批次
        List<WarehouseOutVO.StockBatch> stockBatchList = itemList.stream().map(item -> {
            WarehouseEntryBatchDO batchDO = batchMap.get(item.getWarehouseEntryBatchId());

            WarehouseOutVO.StockBatch stockBatch = new WarehouseOutVO.StockBatch();
            stockBatch.setWarehouseEntrySn(item.getWarehouseEntrySn());
            stockBatch.setBatchId(item.getWarehouseEntryBatchId());
            stockBatch.setBatchSn(item.getWarehouseEntryBatchSn());
            stockBatch.setRemainNum(batchDO.getRemainNum());
            stockBatch.setOutNum(item.getOutNum());

            return stockBatch;
        }).collect(Collectors.toList());
        orderItem.setBatchList(stockBatchList);

        return orderItem;
    }

    default WarehouseOutVO.StockBatch convert(WarehouseEntryBatchDO warehouseEntryBatchDO, int batchOutNum){
        WarehouseOutVO.StockBatch stockBatch = new WarehouseOutVO.StockBatch();

        stockBatch.setWarehouseEntrySn(warehouseEntryBatchDO.getWarehouseEntrySn());
        stockBatch.setBatchId(warehouseEntryBatchDO.getId());
        stockBatch.setBatchSn(warehouseEntryBatchDO.getSn());
        stockBatch.setRemainNum(warehouseEntryBatchDO.getRemainNum());
        stockBatch.setOutNum(batchOutNum);

        return stockBatch;
    }

}

